package com.landun.market.service.impl;

import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.LoadingCache;
import com.landun.market.exception.BusinessException;
import com.landun.market.exception.DataNotFoundException;
import com.landun.market.pojo.enums.ResponseCode;
import com.landun.market.pojo.enums.StateCode;
import com.landun.market.pojo.model.User;
import com.landun.market.repository.IUserRepository;
import com.landun.market.service.IUserService;
import com.landun.market.util.NumberUtils;
import com.landun.market.util.PasswordUtils;
import com.landun.market.util.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.Optional;
import java.util.concurrent.TimeUnit;

/**
 * @author jiang.chuan
 * @date 2018-05-12
 */
@Slf4j
@Service
public class UserService implements IUserService {
    private final IUserRepository userRepository;

    // 用户信息缓存
    private final LoadingCache<Integer, User> userCache;

    @Autowired
    public UserService(IUserRepository userRepository) {
        this.userRepository = userRepository;

        userCache = Caffeine.newBuilder()
                .initialCapacity(5).maximumSize(1000)
                .expireAfterAccess(5, TimeUnit.MINUTES)
                .build(userRepository::load);
    }

    @Override
    public User load(Integer userId) {
        return Optional.ofNullable(loadSafely(userId))
                .orElseThrow(() -> new DataNotFoundException(ResponseCode.DATA_NOT_FOUND, "用户", userId));
    }

    @Override
    public User loadSafely(Integer userId) {
        return userCache.get(userId);
    }

    @Override
    public List<User> find(String keyword) {
        return userRepository.findAll(StringUtils.trimToEmpty(keyword));
    }

    @Override
    public boolean isDuplicated(String identifier, Integer excludedId) {
        if (StringUtils.isBlank(identifier)) {
            return false;
        }

        return Optional.ofNullable(userRepository.countByIdentifier(identifier, excludedId))
                .map(count -> count > 0)
                .orElse(false);
    }

    @Override
    @Transactional
    public boolean create(User user) {
        Integer result = userRepository.create(user);
        if (NumberUtils.isPositive(result)) {
            userRepository.updatePassword(user.getId(),
                    PasswordUtils.generateUserPassword(user.getId(), user.getPassword()));
            return true;
        }
        return false;
    }

    @Override
    public boolean update(Integer userId, User user) {
        User existedUser = this.load(userId);
        Integer result = userRepository.update(User.builder()
                .id(existedUser.getId())
                .name(user.getName())
                .nickname(user.getNickname())
                .mobile(user.getMobile())
                .company(user.getCompany())
                .build());

        return NumberUtils.isPositive(result);
    }

    @Override
    public boolean changePassword(Integer userId, String oldPassword, String newPassword) {
        User existedUser = userRepository.load(userId);

        // 检查旧密码
        if (StringUtils.isNotBlank(oldPassword)
                && !StringUtils.equals(existedUser.getPassword(), PasswordUtils.generateUserPassword(userId, oldPassword))) {
            throw new BusinessException("旧密码不正确");
        }

        Integer result = userRepository.updatePassword(userId, PasswordUtils.generateUserPassword(userId, newPassword));
        return NumberUtils.isPositive(result);
    }

    @Override
    public boolean changeState(Integer userId, StateCode stateCode) {
        if (null == stateCode) {
            throw new BusinessException(ResponseCode.PARAMETER_REQUIRED, "状态");
        }

        User existedUser = userRepository.load(userId);
        if (null != existedUser.getReserved() && existedUser.getReserved()) {
            throw new BusinessException("不允许修改保留数据状态");
        }

        Integer result = userRepository.updateState(stateCode.code(), existedUser.getId());
        return NumberUtils.isPositive(result);
    }
}
